Input Devices



Individual assignment:

Measure something: add a sensor to a microcontroller board that you have designed and read it



This week, I decided to go with the MPU6050 sensor module because when I examined the requirements for the final project, I realized that the essential sensor I need is an angle sensor. After some consideration, I concluded that the MPU6050 would be the best option for this purpose.

MPU6050


MPU6050 sensor module is complete 6-axis Motion Tracking Device. It combines 3-axis Gyroscope, 3-axis Accelerometer and Digital Motion Processor all in small package. Also, it has additional feature of on-chip Temperature sensor. It has I2C bus interface to communicate with the microcontrollers.

It has Auxiliary I2C bus to communicate with other sensor devices like 3-axis Magnetometer, Pressure sensor etc.

If 3-axis Magnetometer is connected to auxiliary I2C bus, then MPU6050 can provide complete 9-axis Motion Fusion output.


3-Axis Gyroscope

The MPU6050 consist of 3-axis Gyroscope with Micro Electro Mechanical System(MEMS) technology. It is used to detect rotational velocity along the X, Y, Z axes as shown in below figure.



3-Axis Accelerometer

The MPU6050 features a 3-axis accelerometer employing Micro Electro Mechanical (MEMs) technology. This configuration enables the sensor to detect angles of tilt or inclination along the X, Y, and Z axes, as illustrated in the figure below.


On-chip Temperature Sensor

On-chip temperature sensor output is digitized using ADC.

The reading from temperature sensor can be read from sensor data register.


MPU6050 Module Pinout

  1. VCC : This pin is connected to the positive supply voltage (typically 3.3V or 5V).
  1. GND : This pin is connected to the ground or 0V reference.
  1. SCL : Serial Clock pin, used for I2C communication.
  1. SDA : Serial Data pin, used for I2C communication.
  1. XDA : Auxiliary I2C bus data input/output for connecting external sensors.
  1. XCL : Auxiliary I2C bus clock input/output for connecting external sensors.
  1. AD0 : This pin is used for I2C address selection. Its state determines the least significant bit of the I2C address.
  1. INT : Interrupt output pin, used to indicate data is ready to be read or other events.

Spec of mpu6050


Sensor Parameter Gyroscope Accelerometer Temperature Sensor Supply Voltage
Sensing Axes 3-axis 3-axis N/A N/A
Full-Scale Range ±250, ±500, ±1000, ±2000 dps ±2g, ±4g, ±8g, ±16g N/A N/A
Sensitivity 131, 65.5, 32.8, 16.4 LSBs/dps 16384, 8192, 4096, 2048 LSBs/g 340 LSBs/°C N/A
Output Data Rate (ODR) 8kHz to 1.25Hz 8kHz to 1.25Hz N/A N/A
Operating Temperature Range N/A N/A -40°C to +85°C N/A
Temperature Sensor Accuracy N/A N/A ±3°C N/A
Supply Voltage Range N/A N/A N/A 2.375V to 3.46V (MPU-6050), 2.375V to 5.5V (MPU-6050A)
Communication Interface I2C serial interface I2C serial interface N/A N/A
Maximum Clock Frequency 400kHz 400kHz N/A N/A
Register Access Modes 8-bit and 16-bit 8-bit and 16-bit N/A N/A
Digital Motion Processor (DMP) Yes N/A N/A N/A
On-Chip ADC Resolution 16-bit 16-bit N/A N/A
Programmable Digital Filters Yes Yes N/A N/A
Interrupts Yes Yes N/A N/A
Low-Power Consumption 3.9mA (full operation) N/A N/A N/A

I2C Communication Protocol


I2C, which stands for Inter-Integrated Circuit, is a bus interface connection protocol that is integrated into devices for serial communication.

It's a commonly used protocol for short-distance communication and is also referred to as Two Wire Interface (TWI).

The I2C communication protocol operates using only 2 bi-directional open-drain lines for data transmission, known as SDA and SCL. Both of these lines are initially pulled high.

I2C operates in 2 modes –

Every data bit transferred on the SDA line is synchronized by a transition from the high to low pulse of each clock signal on the SCL line.



As per the I2C protocols, the data line cannot change while the clock line is high;

It can only change when the clock line is low.

Since the devices on the I2C bus are actively low, pull-up resistors are required to keep lines high.

Data is transmitted in the form of packets consisting of 9 bits. The sequence of these bits includes:


Start and stop conditions

I2C protocols are generated by keeping the SCL line high and changing the level of SDA.

To generate a start condition, the SDA line is transitioned from high to low while keeping SCL high.

Conversely, to generate a stop condition, the SDA line transitions from low to high while keeping the SCL high.

A high Read/Write bit indicates that the master is sending the data to the slave, whereas a low Read/Write bit indicates that the master is receiving data from the slave.

To find the I2C address of the MPU6050 sensor connected

#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
  while (!Serial); // Wait for the serial connection to be established
  Serial.println("\nI2C Scanner");
}

void loop() {
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for (address = 1; address < 127; address++) {
    // The I2C scanner uses the return value of
    // the Wire.endTransmission to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.print(address, HEX);
      Serial.println("  !");

      nDevices++;
    } else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16)
        Serial.print("0");
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");

  delay(5000); // wait 5 seconds for the next scan
}

This program scans all possible I2C addresses and reports any devices it finds


The Serial Monitor will display the I2C addresses of any connected devices. The MPU6050 typically has an I2C address of
0x68 or 0x69

This confirms that the MPU6050 is connected and its I2C address is 0x68 .

If you have any issues or don't see any devices, double-check your connections and ensure that the MPU6050 is properly powered.

From this point, I started working with the ATtiny chipboard that I had created during the output devices week .


Afterward, I began programming the ATtiny board. However,


I soon realized that the ATtiny board couldn't run the required library. Consequently, I shifted my focus to prompting the GPT for code compatible with the ATtiny and MPU6050, aiming to obtain gyroscopic data.


here is the code that i have used

#include <Wire.h>

#define MPU_ADDR 0x68 // MPU-6050 I2C address

// MPU-6050 register addresses
#define REG_ACCEL_XOUT_H 0x3B
#define REG_GYRO_XOUT_H 0x43
#define REG_PWR_MGMT_1 0x6B

void setup() {
  Wire.begin(); // Initialize I2C communication
  Serial.begin(9600); // Initialize serial communication

  // Wake up MPU-6050
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(REG_PWR_MGMT_1);
  Wire.write(0); // Clear sleep mode bit
  Wire.endTransmission(true);
}

void loop() {
  // Read accelerometer data
  int16_t accel_x, accel_y, accel_z;
  readSensor(REG_ACCEL_XOUT_H, &accel_x, &accel_y, &accel_z);

  // Read gyroscope data
  int16_t gyro_x, gyro_y, gyro_z;
  readSensor(REG_GYRO_XOUT_H, &gyro_x, &gyro_y, &gyro_z);

  // Print values
  Serial.print("Acceleration X: ");
  Serial.print(a.acceleration.x);
  Serial.print(", Y: ");
  Serial.print(a.acceleration.y);
  Serial.print(", Z: ");
  Serial.print(a.acceleration.z);
  Serial.println(" m/s^2");
  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.println(" rad/s");
  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.println(" degC");
  Serial.println("");
  delay(1000);
}

void readSensor(uint8_t reg_addr, int16_t* x, int16_t* y, int16_t* z) {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(reg_addr);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_ADDR, 6, true);

  *x = (Wire.read() << 8) | Wire.read();
  *y = (Wire.read() << 8) | Wire.read();
  *z = (Wire.read() << 8) | Wire.read();
}

Initially, I faced issues getting accurate values from the gyroscope. After utilizing the interrupt pin, the values became more precise.


I had to mill another board because the board from the Output week didn't have an interrupt pin. So, I designed and milled a new board, this time using the SAMD21 IC, as it has enough space for the necessary libraries also.


This is the output in the new board that i have created




When considering the final project, I realized that adding a servo to the setup would complete the electronics portion.

Therefore, I decided to test the code I had initially written. I began by using the Arduino Uno that I had on hand. I connected both the MPU6050 and the servo motor to it, and then attempted to program them together.


Since I'm not particularly skilled at programming, I sought the assistance of ChatGPT to help me with the coding process. After continuously prompting ChatGPT for suggestions, I eventually pieced together something that seemed like it would work for the project.


#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
#include <Servo.h>

MPU6050 mpu;
Servo servo;
Quaternion q;           // quaternion container
VectorFloat gravity;    // gravity vector

#define INTERRUPT_PIN 2  // use pin 2 on Arduino Uno & most boards

bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
volatile bool mpuInterrupt = false;

float initialYaw = 0.0; // initial yaw value for calibration
bool calibrated = false; // flag to indicate if calibration is done

void setup() {
  Wire.begin();
  Serial.begin(38400);
  while (!Serial);

  mpu.initialize();
  pinMode(INTERRUPT_PIN, INPUT);
  devStatus = mpu.dmpInitialize();
  mpu.setXGyroOffset(17);
  mpu.setYGyroOffset(-69);
  mpu.setZGyroOffset(27);
  mpu.setZAccelOffset(1551);

  if (devStatus == 0) {
    mpu.setDMPEnabled(true);
    attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();
    dmpReady = true;
    packetSize = mpu.dmpGetFIFOPacketSize();
  }

  servo.attach(9); // Change to the pin your servo is connected to
}

void loop() {
  if (!dmpReady) return;

  while (!mpuInterrupt && fifoCount < packetSize) {
    if (mpuInterrupt && fifoCount < packetSize) {
      fifoCount = mpu.getFIFOCount();
    }
  }

  mpuInterrupt = false;
  mpuIntStatus = mpu.getIntStatus();
  fifoCount = mpu.getFIFOCount();

  if ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) {
    mpu.resetFIFO();
    fifoCount = mpu.getFIFOCount();
    Serial.println(F("FIFO overflow!"));
  } else if (mpuIntStatus & _BV(MPU6050_INTERRUPT_DMP_INT_BIT)) {
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    fifoCount -= packetSize;

    float ypr[3];
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
ypr[0] = ypr[0] * 180 / M_PI;

Serial.print("Yaw angle: ");
Serial.println(ypr[0]); // Print the yaw angle to the serial monitor

if (!calibrated) {
  initialYaw = ypr[0]; // Store initial yaw value for calibration
  calibrated = true;
} else {
  // Check if the yaw value has changed significantly from initial
  if (abs(ypr[0] - initialYaw) > 5) { // You can adjust the threshold angle here
    // Turn servo to 60 degrees
    servo.write(10);
  } else {
    // Return servo to initial position
    servo.write(90); // Adjust this value according to your servo's initial position
  }
}

  }
}

void dmpDataReady() {
  mpuInterrupt = true;
}

I started by sourcing an example code for the MPU6050 and then combined it with the code for the servo motor. Afterward, I reviewed the code and removed any unnecessary parts to streamline it for my project.






Group assignment: click here

Probe an input device's analog levels and digital signals

Download Files

download_filespcb